#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include<unordered_map>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

extern char**environ;
#define CS 1024//命令行提示符最大值
#define FT "my simulate shell:%s@%s %s# "//snprintf的format最大值
#define sp " "//space
#define MC 128//命令行参数最大值



//这里模拟了bash的两张表，而不是main直接继承下（改变environ指针）而是重新布置了一下数组，让environ指针指向我们所布置的数组。
// 1. 命令行参数表
#define MAXARGC 128
char *argv[MAXARGC];
int argc = 0;

// 2. 环境变量表
#define MAX_ENVS 100
char *env[MAX_ENVS];
int envs = 0;

char cwd[1024];
char cwdenv[1029];
 //char *lastpwd=(char*)calloc(1024,1);
std::string lastpwd;
int lastcode=0;

//char export_env[1024];
std::string env_str;
//对alias的适用：
std::unordered_map<std::string,std::string>_alias;
std::string cur,pre;
std::string hash_cp;

//重定向：
std::string filename;
#define NOPUT_RE 0
#define INPUT_RE 1
#define OUTPUT_RE 2
#define APPPUT_RE 3
int status;//重定向方式

void initenv(){
    memset(env, 0, sizeof(env));
    envs= 0;

    for(int i=0;environ[i];i++){
        env[i]=(char*)malloc(strlen(environ[i])+1);//这里模拟的也可以不开空间，直接栈上
        strcpy(env[i], environ[i]);
        envs++;
    }
        env[envs] = NULL;

    
    // for(int i = 0; env[i]; i++)
   // {
   //     putenv(env[i]);
   // }
    environ = env;//用自己生成的env表
}

 //获取一些环境变量：
const char* getuser(){
    const char*u=getenv("USER");
    return u==NULL?"NONE":u;
}

const char* gethostname(){
    const char*h=getenv("HOSTNAME");
    return h==NULL?"NONE":h;
}

//const char* getpwd(){
//    const char*p=getenv("PWD");
//    return p==NULL?"NONE":p;
//}
const char *gethome()
{
    const char *home = getenv("HOME");
    return home == NULL ? "" : home;
}
//这里获得环境变量和其他上面不同；因为当我们通过chdir改变当前目录的时候它在环境变量中的记录（真正的bash实现了）而我们没有实现，因此我们
//可以通过getcwd每次调完新的目录开始就使用它不仅能改变了env的pwd也就是新的位置；还能打印命令行提示符的时候变化
const  char *getpwd(){
        char *pwd = getcwd(cwd, sizeof(cwd));
    if(pwd != NULL)
    {
        snprintf(cwdenv, sizeof(cwdenv), "PWD=%s", cwd);
        putenv(cwdenv);//put进去的是环境变量的地址也就是一个指针指向的是那段量，因此指针要么是全局要么在堆上
    }
    return  pwd==NULL?"NONE":pwd;
}
//const char *getpwd(){
//    // 调用了这个，在Getpwd中
//    return Getpwd();
//}
//打印命令行提示符：把pwd的最后一个名称得到
std::string convert(const char *pwd){
    std::string s=pwd;
  auto pos=s.rfind('/');
  if(pos==std::string::npos)return "error";
  if(pos==s.size()-1)return "/";
  else return s.substr(pos+1);


  
}
void ptcmdprompt(){
    char p[CS]={0};
    snprintf(p,sizeof(p),FT, getuser(),gethostname(),convert(getpwd()).c_str() );
    printf("%s",p);
    //无\n及时刷新标准输出缓冲区
    fflush(stdout);
}
//获得命令行参数：
bool gtcmdline(char *p,int size){
  char *j=fgets(p,size,stdin);
 // printf("参数是：%d",size);
  if(j==NULL)return false;
  //printf("%s\n",p);
  p[strlen(p)-1]=0;//干掉\n
  if(strlen(p)==0) return false;
  else return true;
}
//命令行解释：把输入的命令行参数分出来方便后序传给要调用的main的argv
bool  cmdparse(char *c){
   // printf("%s\n",c);
    argc=0;
   std::string cc=c;
 //  std::cout<<"内容:"<<_alias[cc]<<std::endl;
 //  浅拷贝：
 //   if(_alias[cc]!="")  c = &((_alias[cc])[0]);
 
 //   借助string的深拷贝赋值完成对hash内数据的深拷贝：
   if(_alias[cc]!=""){
       hash_cp =_alias[cc];
       c=&(hash_cp[0]);

   }  
   //printf("%s\n",c);
      argv[argc++]=strtok(c,sp);//此处c这个指针将会随之变化最后分割结束为null
   while(argv[argc++]=strtok(NULL,sp)){}
     argc--;
   //printf("%s\n%s\n",argv[0],argv[1]);
  // printf("%s%s\n",argv[0],argv[1]);
   return true;
}
 
//void lastpwd(){
//   // printf("11111111111111111");
// //  printf(" %s%d\n ",argv[0],argc);
//   if(!strcmp(argv[0],"cd")&&argc==2){
//        // printf("执行\n");
//        std::string s("LASTPWD");
//        s+="=";
//        s+=argv[1];
//        //std::cout<<s<<std::endl;
//        // char p[s.size()+1]={0};
//        char* p = (char*)calloc(s.size() + 1, 1);
//        for(int i=0;i<s.size();i++) p[i]=s[i];
//        printf("huanbian:%s\n",p);
//        environ[envs]=(char*)calloc(strlen(p)+1,1);
//        putenv(p);
//   }
//}
bool Cd(){
if(argc == 1)
    //直接返回到家目录，但是此时没有更改env的pwd，故我们后面调用getpwd（）完成更改env标记  借助string完成了否则对返回const多次覆盖保存较为麻烦
    {
        std::string home = gethome();
        if(home.empty()) return false;
       std::string tmp=getpwd();
                lastpwd=tmp;

        chdir(home.c_str());
           getpwd();
            return true;
    }
else{
     std::string where = argv[1];
    // printf("%s\n",argv[1]);
        // cd - / cd ~
        if(where=="-") //上一个工作目录
        {   // std::cout<<lastpwd<<std::endl;

            chdir(lastpwd.c_str());//这里的lastpwd是我们在新切换目录更改env前记录的；故是先前的pwd
              getpwd();

        }
            
          else if(where=="/"){
             std::string tmp=getpwd();
                lastpwd=tmp;
                chdir("/");
                getpwd();

            }
            else if(where=="~")//家目录
        {
            if(!strcmp(getuser(),"root")){ 
                 std::string tmp=getpwd();
                lastpwd=tmp;

                chdir("/root");
                getpwd();
            }
            else {
               std::string home = gethome();
               std::string tmp=getpwd();
                lastpwd=tmp;

                  chdir(home.c_str());
                  getpwd();
            }
        }

 //  else if(where==".."){} 上级目录
         
        else
        {     std::string tmp=getpwd();
                lastpwd=tmp;
            // std::cout<<lastpwd<<std::endl;
             chdir(where.c_str());
             getpwd();
        }
           return true;

       }

}
void Echo(){
//echo $? echo $PATH
    if(argc==2){
   std::string func=argv[1];
   if(func=="$?"){
        std::cout << lastcode << std::endl;
         lastcode = 0;
   }
   else if(func[0]=='$'){
       std::string envname = func.substr(1);
        const char * envvalue= getenv(envname.c_str());
            if(envvalue)
                std::cout << envvalue << std::endl;
   }
     else
        {
            std::cout << func << std::endl;
        }
     
}
}
void Export(){
   env_str=argv[1];
        env[envs]=(char*)calloc(strlen(argv[1])+1,1);
            for(int i=0;i<env_str.size();i++)env[envs][i]=env_str[i];
            envs++;
            env[envs]=NULL;

}
void Alias(){
 std::string sec=argv[1];
      auto pos=sec.find('=');
        cur=sec.substr(0,pos);
       pre=sec.substr(pos+1,std::string::npos);
       for(int i=2;i<argc;i++){
           pre+=" ";
           pre+=argv[i];
       }
      _alias[cur]=pre;

}
//分子进程执行：
int  execute(){
  pid_t pid=fork();
  if(pid==0){
    //printf("argv[0]: %s\n", argv[0]);
    
      if(status==INPUT_RE){
          int fd=open(filename.c_str(),O_RDONLY);
          if(fd < 0) exit(1);
            dup2(fd,0);
            close(fd);
      }

      else if(status==OUTPUT_RE){
           int fd=open(filename.c_str(),O_CREAT | O_WRONLY | O_TRUNC, 0666);
          if(fd < 0) exit(1);
            dup2(fd,1);
            close(fd);
      }
          else if(status==APPPUT_RE){
          int fd=open(filename.c_str(),O_CREAT | O_WRONLY | O_APPEND, 0666);
          if(fd < 0) exit(1);
            dup2(fd,1);
            close(fd);

          }
      else { }//NOPUT_RE无重定向操作
      
   execvp(argv[0],argv);
   exit(1);
  }
  int status=0;
  pid_t id=waitpid(pid,&status,0);
  if(id > 0)
    {
        lastcode = WEXITSTATUS(status);//对于这里规定当execute的子进程执行完就返回1；内建命令或者其他都是返回0
    }
    return 0;
   

}
//内建命令：和execute执行是分开的
bool checkinkeycmd()
{
    std::string cmd = argv[0];
    if(cmd == "cd")
    {
       // printf("cd进入!\n");
        Cd();
        return true;
    }
    else if(cmd == "echo")
    {
     Echo();
        return true;
    }
    else if(cmd == "export")
    {
         Export();
    }
    else if(cmd == "alias"&&argc>=2)
    {
       Alias();
    }

    return false;
}

void eliminatesp(char cl[],int &fg){
    while(isspace(cl[fg])) fg++;
    
}
void redirect(char cl[]){
      status=NOPUT_RE;
      int start=0;
      int end=strlen(cl)-1;
      while(start<end){
          if(cl[end]=='<'){
              cl[end++] = 0;
            eliminatesp(cl, end);
            status = INPUT_RE;
            filename = cl+end;
            break;
          }
          else if(cl[end]=='>'){
              if(cl[end-1]=='>'){
                 status=APPPUT_RE;
                 cl[end-1]=0;
              }
              else{
                  status=OUTPUT_RE;
                  cl[end]=0;
              }
             end++;
             eliminatesp(cl,end);
              filename = cl+end;
                 break;
          }
          else{
              end--;
          }
      }
    
}
void destroy(){
    for(int i=0;env[i];i++){
        free(env[i]);
    }
}

int main() {
    //自己的环境变量和命令行参数表的初始化：
    initenv();

    while(1) {
        //命令提示行打印：
        ptcmdprompt();

        char cl[CS]={0};
        //把命令参数输入到cl
        while(!gtcmdline(cl,sizeof(cl))){}

       redirect(cl);

       //把命令参数这个串拆解到argv里：
        cmdparse(cl);
     //判断是否是内建命令由bash自己完成（这里模拟的是main自己执行）
        if(checkinkeycmd()) {
           // lastpwd();
            continue;
        } 
        execute();
    }
    //销毁表所开辟的空间
    destroy();
}
